package org.kxml.io;

import java.io.IOException;
import java.io.Writer;

import org.kxml.PrefixMap;
import org.kxml.Xml;

/** An abstract XmlWriter including namespace handling. */

public abstract class AbstractXmlWriter extends Writer {

    protected State current = new State(null, PrefixMap.DEFAULT, // null,
                                                                    // null,
            null);

    /**
     * writes an attribute. Only allowed immediately after startTag or
     * attribute.
     */

    public abstract void attribute(String name, String value)
            throws IOException;

    /**
     * writes an attribute with the given namespace. Only allowed immediately
     * after startTag or another attribute call.
     */

    public void attribute(String namespace, String name, String value)
            throws IOException {

        if (namespace == null || "".equals(namespace))
            attribute(name, value);
        else {
            String prefix = current.prefixMap.getPrefix(namespace);
            if (prefix == null || prefix.equals("")) {
                int cnt = 0;
                do {
                    prefix = "p" + (cnt++);
                } while (current.prefixMap.getNamespace(prefix) != null);
                current.prefixMap = new PrefixMap(current.prefixMap, prefix,
                        namespace);

                attribute("xmlns:" + prefix, namespace);
            }
            attribute(prefix + ":" + name, value);
        }
    }

    /**
     * Abstract method for writing an end tag. <b>Attention:</b> Concrete
     * implementations must pop the previous stack from the stack:<br />
     * <br />
     * 
     * current = current.prev;
     */

    public abstract void endTag() throws IOException;

    public PrefixMap getPrefixMap() {
        return current.prefixMap;
    }

    /**
     * abstract method that must be overwritten by a method actually writing the
     * resolved start tag without namespace checking. This implementation just
     * puts the state on the stack.<br />
     * <br />
     * 
     * Attention: The actual implementation include the following line in order
     * to put the current State on the stack!<br />
     * <br />
     * 
     * current = new State (current, prefixMap, tag);
     */

    protected abstract void startTag(PrefixMap prefixMap, String tag)
            throws IOException;

    /**
     * writes a start tag with the given name, using the given prefix map. This
     * method cares about the namespace prefixes and calls startTag (PrefixMap
     * prefixMap, String tag) for concrete writing.
     */

    public void startTag(PrefixMap prefixMap, String namespace, String name)
            throws IOException {

        // check if namespace is default.

        if (prefixMap == null)
            prefixMap = current.prefixMap;
        if (namespace == null)
            namespace = "";

        String prefix = prefixMap.getPrefix(namespace);

        if (prefix == null) {
            // System.out.println ("namespace "+namespace +" not found in
            // "+prefixMap);

            prefixMap = new PrefixMap(prefixMap, "", namespace);
            prefix = "";
        }

        String tag = prefix.length() == 0 ? name : prefix + ':' + name;

        PrefixMap oldMap = current.prefixMap;

        startTag(prefixMap, tag);

        // if namespace has changed, write out changes...

        if (prefixMap != oldMap) {
            PrefixMap current = prefixMap;
            do {
                String p2 = current.getPrefix();
                String ns = current.getNamespace();
                if (prefixMap.getNamespace(p2).equals(ns)
                        && !ns.equals(oldMap.getNamespace(p2)))
                    attribute(p2.equals("") ? "xmlns" : ("xmlns:" + p2), ns);
                current = current.getPrevious();
            } while (current != null && current != oldMap);
        }
    }

    /** convenience method for startTag (Xml.NO_NAMESPACE, name) */

    public void startTag(String name) throws IOException {
        startTag(null, Xml.NO_NAMESPACE, name);
    }

    /** writes a start tag with the given namespace and name */

    public void startTag(String namespace, String name) throws IOException {
        startTag(null, namespace, name);
    }

    /** writes Xml.DOCTYPE, Xml.PROCESSING_INSTRUCTION or Xml.COMMENT */

    public abstract void writeLegacy(int type, String text) throws IOException;
}
